package com.forum.module.system.service.user;

import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.forum.framework.common.enums.CommonStatusEnum;
import com.forum.framework.common.exception.util.ServiceExceptionUtils;
import com.forum.framework.common.pojo.PageResult;
import com.forum.framework.common.util.object.BeanUtils;
import com.forum.framework.security.config.SecurityProperties;
import com.forum.framework.security.core.util.SecurityUtils;
import com.forum.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
import com.forum.module.system.controller.admin.user.vo.user.ForgotPasswordReqVO;
import com.forum.module.system.controller.admin.user.vo.user.UserPageReqVO;
import com.forum.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import com.forum.module.system.controller.admin.user.vo.user.UserUpdatePasswordReqVO;
import com.forum.module.system.dal.dataobject.user.UserDO;
import com.forum.module.system.dal.dataobject.user_role.UserRoleDO;
import com.forum.module.system.dal.mysql.user.UserMapper;
import com.forum.module.system.dal.mysql.user_role.UserRoleMapper;
import com.forum.module.system.enums.logger.LoginResultEnum;
import com.forum.module.system.service.mail.MailService;
import com.forum.module.system.service.oauth2.OAuth2TokenService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.catalina.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

import static com.forum.module.system.enums.ErrorCodeConstants.*;
import static com.forum.module.system.enums.ErrorCodeConstants.USER_EMAIL_EXISTS;

/**
 * 后台用户 Service 实现类
 *
 * @author zihan.ouyang
 */
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {
    private final UserMapper userMapper;

    private final UserRoleMapper userRoleMapper;

    private final MailService mailService;

    private final SecurityProperties securityProperties;

    private final OAuth2TokenService oauth2TokenService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public UserServiceImpl(UserMapper userMapper,
                           UserRoleMapper userRoleMapper,
                           MailService mailService,
                           SecurityProperties securityProperties,
                           OAuth2TokenService oauth2TokenService) {
        this.userMapper = userMapper;
        this.userRoleMapper = userRoleMapper;
        this.mailService = mailService;
        this.securityProperties = securityProperties;
        this.oauth2TokenService = oauth2TokenService;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createUser(UserSaveReqVO createReqVO) {
        // 1.1 校验参数
        validateUserForCreateOrUpdate(null, createReqVO.getUsername(), createReqVO.getEmail(), createReqVO.getMobile());
        // 2. 插入用户
        UserDO userDO = BeanUtils.toBean(createReqVO, UserDO.class);
        userDO.setStatus(CommonStatusEnum.NORMAL.getStatus());
        userDO.setPassword(SecurityUtils.encryptPassword(createReqVO.getPassword()));
        userMapper.insert(userDO);

        handleRole(userDO);

        return userDO.getId();
    }

    @Override
    public UserDO getUserByUsername(String username) {
        return userMapper.selectByUsername(username);
    }

    @Override
    public UserDO getUser(Long userId) {
        return userMapper.selectById(userId);
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return SecurityUtils.matchesPassword(rawPassword, encodedPassword);
    }

    @Override
    public Long registerUser(AuthRegisterReqVO createReqVO) {
        // 1.1 校验正确性
        validateUserForCreateOrUpdate(null, createReqVO.getUsername(), null, null);

        // 2. 插入用户
        UserDO user = BeanUtils.toBean(createReqVO, UserDO.class);
        user.setStatus(CommonStatusEnum.NORMAL.getStatus()); // 默认开启
        user.setPassword(SecurityUtils.encryptPassword(createReqVO.getPassword())); // 加密密码
        userMapper.insert(user);

        handleRole(user);
        return user.getId();
    }

    private void handleRole(UserDO userDO) {
        UserRoleDO userRoleDO = new UserRoleDO();
        userRoleDO.setUserId(userDO.getId());
        userRoleDO.setRoleId(2L);

        userRoleMapper.insert(userRoleDO);
    }

    @Override
    public void updateUser(UserSaveReqVO updateReqVO) {
        updateReqVO.setPassword(null); // 特殊：此处不更新密码
        // 1. 校验正确性
        validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
                updateReqVO.getMobile(), updateReqVO.getEmail());

        // 2.1 更新用户
        UserDO updateObj = BeanUtils.toBean(updateReqVO, UserDO.class);
        userMapper.updateById(updateObj);
    }

    @Override
    public void updateUserPassword(UserUpdatePasswordReqVO reqVO, HttpServletRequest request) {
        // 1. 校验用户存在
        UserDO user = validateUserExists(reqVO.getEmail());

        // 2. 校验当前密码与旧密码是否一致与新密码是否相同
        validatePwdAndNewPwd(reqVO.getPassword(), reqVO.getNewPassword(), user);

        // 3. 校验邮箱验证码
        validEmailCode(reqVO, request, user);
    }

    private void validatePwdAndNewPwd(String password, String newPassword, UserDO userDO) {
        if (!isPasswordMatch(password, userDO.getPassword())) {
            throw ServiceExceptionUtils.exception(USER_PASSWORD_ERROR);
        }
        if (password.equals(newPassword)) {
            throw ServiceExceptionUtils.exception(PWD_NEWPWD_NOT_EQUALS);
        }
    }

    @Override
    public void updateUserStatus(Long id, Integer status) {
        // 1. 校验用户存在
        validateUserExistsById(id);

        // 2. 更新状态
        UserDO updateObj = new UserDO();
        updateObj.setId(id);
        updateObj.setStatus(status);
        userMapper.updateById(updateObj);
    }

    @Override
    public void deleteById(Long id) {
        // 1. 校验用户存在
        validateUserExistsById(id);

        // 2.1 删除用户
        userMapper.deleteById(id);

        // 2.2 删除用户关联数据
    }

    @Override
    public PageResult<UserDO> getUserPage(UserPageReqVO pageReqVO) {
        return userMapper.selectPage(pageReqVO);
    }

    @Override
    public List<UserDO> getUserListByStatus(Integer status) {
        return userMapper.selectListByStatus(status);
    }

    @Override
    public void updateUserLogin(Long userId, String clientIP) {
        userMapper.updateById(new UserDO().setId(userId).setLoginIp(clientIP).setLoginDate(LocalDateTime.now()));
    }

    @Override
    public void forgotUserPassword(ForgotPasswordReqVO reqVO, HttpServletRequest request) {
        // 1. 校验用户存在
        UserDO user = validateUserExists(reqVO.getEmail());
        UserUpdatePasswordReqVO userUpdatePasswordReqVO = BeanUtils.toBean(reqVO, UserUpdatePasswordReqVO.class);

        // 2. 校验邮箱验证码
        validEmailCode(userUpdatePasswordReqVO, request, user);
    }

    private void validEmailCode(UserUpdatePasswordReqVO reqVO, HttpServletRequest request, UserDO user) {
        if (mailService.validMail(reqVO.getEmail(), reqVO.getEmailCode())) {
            // 1.更新密码
            UserDO updateObj = new UserDO();
            updateObj.setId(user.getId());
            updateObj.setPassword(SecurityUtils.encryptPassword(reqVO.getNewPassword())); // 加密密码
            userMapper.updateById(updateObj);

            // 2. 删除Token
            String token = SecurityUtils.getToken(request, securityProperties.getHeaderName(),
                    securityProperties.getTokenParameter());
            if (token != null) {
                oauth2TokenService.removeAccessToken(token);
            }

            // 3. 删除验证码
            redisTemplate.delete(reqVO.getEmail());
        } else {
            throw ServiceExceptionUtils.exception(MAIL_CODE_NOT_EQUALS);
        }
    }

    private UserDO validateUserForCreateOrUpdate(Long id, String username, String email, String mobile) {
        // 校验用户存在
        UserDO userDO = validateUserExistsById(id);
        // 校验用户名唯一性
        validateUsernameUnique(id, username);
        // 校验邮箱唯一性
        validateEmailUnique(id, email);
        // 校验手机号唯一性
        validateMobileUnique(id, mobile);
        return userDO;
    }

    @VisibleForTesting
    UserDO validateUserExists(String email) {
        if (StrUtil.isBlank(email)) {
            return null;
        }
        UserDO userDO = userMapper.selectByEmail(email);
        if (userDO == null) {
            throw ServiceExceptionUtils.exception(USER_NOT_EXISTS);
        }
        return userDO;
    }

    @VisibleForTesting
    UserDO validateUserExistsById(Long id) {
        if (id == null) {
            return null;
        }
        UserDO userDO = userMapper.selectById(id);
        if (userDO == null) {
            throw ServiceExceptionUtils.exception(USER_NOT_EXISTS);
        }
        return userDO;
    }

    @VisibleForTesting
    void validateUsernameUnique(Long id, String username) {
        if (StrUtil.isBlank(username)) {
            return;
        }
        UserDO user = userMapper.selectByUsername(username);
        if (user == null) {
            return;
        }
        if (id == null) {
            throw ServiceExceptionUtils.exception(USER_USERNAME_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw ServiceExceptionUtils.exception(USER_USERNAME_EXISTS);
        }
    }

    @VisibleForTesting
    void validateEmailUnique(Long id, String email) {
        if (StrUtil.isBlank(email)) {
            return;
        }
        UserDO user = userMapper.selectByEmail(email);
        if (user == null) {
            return;
        }
        if (id == null) {
            throw ServiceExceptionUtils.exception(USER_EMAIL_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw ServiceExceptionUtils.exception(USER_EMAIL_EXISTS);
        }
    }

    @VisibleForTesting
    void validateMobileUnique(Long id, String mobile) {
        if (StrUtil.isBlank(mobile)) {
            return;
        }
        UserDO user = userMapper.selectByMobile(mobile);
        if (user == null) {
            return;
        }
        if (id == null) {
            throw ServiceExceptionUtils.exception(USER_MOBILE_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw ServiceExceptionUtils.exception(USER_MOBILE_EXISTS);
        }
    }
}
